﻿using System;

namespace ComputationalGeometry.Shapes
{
    public class Point2D : IEquatable<Point2D>
    {
        private double _x;

        private double _y;

        /// <summary>
        /// Point2D Constructor.
        /// </summary>
        /// <param name="x">X coord.</param>
        /// <param name="y">Y coord.</param>
        public Point2D(double x, double y)
        {
            _x = x;
            _y = y;
        }

        /// <summary>
        /// X coordinate of 2D point
        /// </summary>
        public double X
        {
            get { return _x; }
            set { _x = value; }
        }

        /// <summary>
        /// Y coordinate of 2D point
        /// </summary>
        public double Y
        {
            get { return _y; }
            set { _y = value; }
        }

        /// <summary>
        /// Operator == overload.
        /// </summary>
        /// <param name="left">Left point of comparison.</param>
        /// <param name="right">Right point of comparison.</param>
        /// <returns>True if both object's data is equal.</returns>
        public static bool operator == (Point2D left, Point2D right)
        {
            return (left.X == right.X && left.Y == right.Y);
        }

        /// <summary>
        /// Operator != overload.
        /// </summary>
        /// <param name="left">Left point of comparison.</param>
        /// <param name="right">Right point of comparison.</param>
        /// <returns>True if objects' data are dissimilar.</returns>
        public static bool operator != (Point2D left, Point2D right)
        {
            return (left.X != right.X || left.Y != right.Y);
        }

        /// <summary>
        /// Performs a comparison to a peer Point2D object.
        /// </summary>
        /// <param name="v">Point2D object to compare to.</param>
        /// <param name="type">The type of comparison to perform.</param>
        /// <returns></returns>
        public int CompareTo(Point2D v, PointComparisonType type)
        {
            switch (type)
            {
                case PointComparisonType.CompareX:
                    return X.CompareTo(v.X);
                case PointComparisonType.CompareY:
                    return Y.CompareTo(v.Y);
                case PointComparisonType.CompareXThenY:
                    return X.CompareTo(v.X) != 0 ? X.CompareTo(v.X) : Y.CompareTo(v.Y);
                case PointComparisonType.CompareXThenYReverse:
                    return X.CompareTo(v.X) != 0 ? X.CompareTo(v.X) : v.Y.CompareTo(Y);
                default:
                    return X.CompareTo(v.X);
            }
        }

        ////public int CompareTo(object obj)
        ////{
        ////    if (obj is Point2D)
        ////    {
        ////        Point2D v = obj as Point2D;

        ////        return v.CompareTo(v, PointComparisonType.CompareX);
        ////    }
        ////    else
        ////        throw new NotImplementedException();
        ////}
        public bool Equals(Point2D other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return other._x.Equals(_x) && other._y.Equals(_y);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Point2D)) return false;
            return Equals((Point2D) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return (_x.GetHashCode()*397) ^ _y.GetHashCode();
            }
        }
    }
}